package zasm.zasm.listing;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Arrays;

import zasm.zasm.util.ValidationUtils;

public abstract class TextListingWriter extends AbstractListingWriter {

	public static final int		MAX_COLUMN_WIDTH		= 256;

	private static final char[]	SPACES					= new char[MAX_COLUMN_WIDTH];
	private static final char[]	TABS					= new char[MAX_COLUMN_WIDTH];
	private static final char[]	ZEROS					= new char[(Long.SIZE / 4) - 1];

	static {
		Arrays.fill(SPACES, ' ');
		Arrays.fill(TABS, '\t');
		Arrays.fill(ZEROS, '0');
	}

	private static final String	COLUMN_STRING			= "column";
	private static final String	TARGET_COLUMN_STRING	= "target column";

	private final Writer		writer;
	private final int			tabWidth;
	private final boolean		useSpaces;

	private int					column					= 0;

	public TextListingWriter(final Writer writer, final int tabWidth, final boolean useSpaces) {
		this.writer = (writer instanceof BufferedWriter) ? writer : new BufferedWriter(writer);
		this.tabWidth = tabWidth;
		this.useSpaces = useSpaces;
	}

	public TextListingWriter(final File file, final int tabWidth, final boolean useSpaces) throws FileNotFoundException {
		this(new BufferedWriter(new PrintWriter(file)), tabWidth, useSpaces);
	}

	public TextListingWriter(final String fileName, final int tabWidth, final boolean useSpaces)
			throws FileNotFoundException {
		this(new File(fileName), tabWidth, useSpaces);
	}

	protected int getColumn() {
		return column;
	}

	protected void setColumn(final int column) throws IOException {
		ValidationUtils.validateArgMin(column, this.column, COLUMN_STRING, TARGET_COLUMN_STRING);
		if (useSpaces) {
			write(SPACES, 0, column - this.column);
		} else {
			write(TABS, 0, (((column - this.column) + tabWidth) - 1) / tabWidth);
		}
	}

	protected void writeDecInt(final int x, final int width) throws IOException {
		final String decString = Integer.toString(x);
		if (decString.length() < width) {
			write(SPACES, 0, width - decString.length());
			write(decString);
		} else {
			write(decString, decString.length() - width, width);
		}
	}

	protected void writeHexInt(final int x, final int width) throws IOException {
		final String hexString = Integer.toHexString(x);
		if (hexString.length() < width) {
			write(ZEROS, 0, width - hexString.length());
			write(hexString);
		} else {
			write(hexString, hexString.length() - width, width);
		}
	}

	protected void writeln() throws IOException {
		writer.write(System.lineSeparator());
		column = 0;
	}

	protected void write(final int c) throws IOException {
		writer.write(c);
		column++;
	}

	protected void write(final char[] cbuf) throws IOException {
		writer.write(cbuf);
		column += cbuf.length;
	}

	protected void write(final char[] cbuf, final int off, final int len) throws IOException {
		writer.write(cbuf, off, len);
		column += len;
	}

	protected void write(final String str) throws IOException {
		writer.write(str);
		column += str.length();
	}

	protected void write(final String str, final int off, final int len) throws IOException {
		writer.write(str, off, len);
		column += len;
	}

	protected TextListingWriter append(final CharSequence csq) throws IOException {
		writer.append(csq);
		column += csq.length();
		return this;
	}

	protected TextListingWriter append(final CharSequence csq, final int start, final int end) throws IOException {
		writer.append(csq, start, end);
		column += (end - start) + 1;
		return this;
	}

	protected TextListingWriter append(final char c) throws IOException {
		writer.append(c);
		column++;
		return this;
	}

	@Override
	public final void flush() throws IOException {
		writer.flush();
	}

	@Override
	public final void close() throws IOException {
		finish();
		writer.close();
	}

	protected void finish() {}

}
